Quick Intro to R Notebooks and R Markdown
This is an R Markdown Notebook. It combines markdown, a plain text
formatting syntax, and R code. When you execute R code within the
notebook, the output appears beneath the code.
This file was created in RStudio by going to File…New File…R
Notebook.
R code needs to be in “chunks” for it to work. Below is an example of
an R code chunk. It makes a parabola using base R graphics (not
ggplot2).
Try executing this chunk by clicking the Run button within
the chunk or by placing your cursor inside it and pressing
Ctrl+Shift+Enter (Win/Linux) or Cmd+Shift+Return
(Mac).
x <- seq(-1, 1, by = 0.01)
y <- x^2
plot(x, y, type = "l")
To hide the output, click the Expand/Collapse output button. To clear
results (or an error), click the “x”.
You can also press Ctrl+Enter (Win/Linux) or
Cmd+Return (Mac) to run one line of code at a time (instead of
the entire chunk).
Add a new R code chunk by clicking the Insert Chunk button
on the toolbar or by pressing Ctrl+Alt+I (Win/Linux) or
Cmd+Option+I (Mac).
Code along 0
Insert a new R code chunk below and type and run the code: 22/7
Import data
Let’s import our Albemarle County real estate data. We use the
readRDS() function. Recall that an rds file is
simply any R object that’s been saved. In this case we saved a data
frame. Below we read in the rds file via URL. To do this we need to use
the base R url() function to open a connection to the
website where the file is stored.
URL <- "https://github.com/uvastatlab/DJA/raw/main/data/albemarle_homes_2022.rds"
homes <- readRDS(url(URL))
Let’s look at the first few rows:
head(homes)
Variable name definitions:
- YearBuilt: year house was built
- YearRemodeled: year house was remodeled
- Condition: assessed condition of home
- FinSqFt: finished square feet
- Cooling: Central Air indicator
- FP_Open: number of fireplaces
- Bedroom: number of bedrooms
- FullBath: number of full bathrooms (toilet, sink and
bath)
- HalfBath: number of half bathrooms (toilet and sink
only)
- TotalRooms: total rooms in house
- LotSize: size of land on which home is located, in
acres
- LandValue: assessed value of the land
- ImprovementsValue: assessed value of “improvements” to
lot
- TotalValue: total assessed value of home and property
- LastSalePrice: price of home when last sold
- LastSaleDate: date home was last sold
- ESdistrict: the elementary school the home feeds into
- MSdistrict: the middle school the home feeds into
- HSDistrict: the high school the home feeds into
- CensusTract: the census tract the home is located in
- Age: of the house in years as of 2022
- Remodeled: indicator if house has been remodeled (0=no,
1=yes)
- FP: indicator if house has fireplace (0=no, 1=yes)
Load packages we’ll use today
Intro to ggplot2
The ggplot2 package implements The Grammar of Graphics as
defined in the book of the same name by Leland Wilkinson.
It requires your data be in a data frame (or tibble).
Let’s do a quick example. How does the total value of a home relate
to its size in finished square feet? Plot TotalValue versus FinSqFt to
create a scatter plot.

What just happened?
- The
ggplot function creates a plotting area for a data
frame
- The
aes function maps variables in the data frame to
aesthetic properties of geometric shapes
- The
geom_point function specifies we want to use
points
- combine these functions with
+, which adds components
to a plot. The + is not a pipe! It is an operator.
Don’t forget the parentheses on geom_point()!
Most of what we do with ggplot2 takes this general form
ggplot(data) +
aes() +
geom_something()
Or this form, with the aesthetics defined in the
ggplot() function:
ggplot(data, aes()) +
geom_something()
There are many geom_ functions in ggplot2. This naming
convention makes them easier to find using RStudio’s autocomplete
functionality.
It can get much more complicated, but for exploratory visualization
this often sufficient.
Code along 1
Do younger (more recently built) houses have a tendency to be bigger
than older houses? Create a scatterplot of FinSqFt (y-axis) versus Age
(x-axis).
- Try entering
alpha = 1/10 in geom_point().
What does it do?
- Try entering
shape = "." in geom_point().
What does it do?
- Try entering
shape = 1 in geom_point().
What does it do? (see ?points shapes and their numeric
codes)
Reminder: Insert a new code chunk by pressing Ctrl+Alt+I
(Win/Linux) or Cmd+Option+I (Mac).
ggplot(homes) +
aes(x = Age, y = FinSqFt) +
geom_point(alpha = 1/10)

More aesthetics and geoms, and introducing facets
Points not only have positions in a plane, but can be different sizes
and have different shapes and colors. We can “map” variables in our data
frame to these aesthetics. In other words, we let variables define
properties of points such as size, shape, and color.
How does the relationship between TotalValue and FinSqFt differ
between homes with and without central air?
Plot TotalValue versus FinSqFt to create a scatter plot, but also
color the points according to whether or not the homes have central air
(Cooling) using the color argument.
ggplot(homes) +
aes(x = FinSqFt, y = TotalValue, color = Cooling) +
geom_point()

We can also map the size of the points to a variable, such as
LotSize, using the size argument. Notice I have also mapped
the shape of the point to Cooling using the shape
argument.
ggplot(homes) +
aes(x = FinSqFt, y = TotalValue,
shape = Cooling, size = LotSize) +
geom_point()

Notice the overplotting. Perhaps we prefer to see separate
scatterplots for each Cooling category. We can achieve that with
facets. The function to use is facet_wrap() with
the variable that we want to facet on. In this case it’s Cooling. Notice
we precede the variable with a tilde ~. We can read that as
“facet by Cooling”.
ggplot(homes) +
aes(x = FinSqFt, y = TotalValue, size = LotSize) +
geom_point(alpha = 1/2) +
facet_wrap(~ Cooling)

We can also have multiple geoms in a plot. Let’s say we want to add a
smooth trend line that summarizes the relationship between FinSqFt and
TotalValue. Add the geom_smooth()
ggplot(homes) +
aes(x = FinSqFt, y = TotalValue) +
geom_point(alpha = 1/2) +
geom_smooth() +
facet_wrap(~ Cooling)
`geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

Code along 2
How is TotalValue related to LotSize within each high school
district?
Create a scatterplot of TotalValue (y-axis) vs LotSize (x-axis),
faceted by HSDistrict. Add smooth trend lines to summarize the
relationship.
Reminder: Insert a new code chunk by pressing Ctrl+Alt+I
(Win/Linux) or Cmd+Option+I (Mac).
ggplot(homes) +
aes(x = LotSize, y = TotalValue) +
geom_point() +
geom_smooth() +
facet_wrap(~HSDistrict)
`geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

Binning and zooming
Dealing with large amounts to data leads to overplotting. So far
we’ve used alpha values to modify transparency from 0 (invisible) to 1
(solid), facets, and different geometric shapes to help alleviate this.
Another is “binning”. As long as you have the hexbin
package installed, this can be easily accomplished with
geom_hex. This “bins” counts of points into hexagons and
colors the hexagons according to counts. Lighter colors mean higher
counts. Changing the number of bins (default 30 x 30)
decreases/increases the number of hexagons. For example,
bins = 40
# install.packages("hexbin")
ggplot(homes) +
aes(x = FinSqFt, y = TotalValue) +
geom_hex(bins = 40)

Albemarle county has homes with very large TotalValue and LotSize
values. Including them in our plots means we “zoom out” to accommodate
them which means we’re “far away” from the majority of homes.
To zoom in on data with ggplot2, we use the
coord_cartesion() function and define the limits to view
with the xlim and/or ylim arguments.
ggplot(homes) +
aes(x = FinSqFt, y = TotalValue) +
geom_point() +
geom_smooth() +
coord_cartesian(xlim = c(0,5000),
ylim = c(0, 2e6)) # from 0 to $2,000,000
`geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

If presenting this plot to an audience, tell them it’s zoomed in and
looking at a subset of your data.
The numbers are a little hard to read on the y-axis. We can format
them as dollar amounts using the labels argument in the
scale_y_continuous() function. To do this we can use the
dollar function from the scales package, which is installed
when you install the ggplot2 package.
ggplot(homes) +
aes(x = FinSqFt, y = TotalValue) +
geom_point() +
geom_smooth() +
coord_cartesian(xlim = c(0, 5000),
ylim = c(0, 2e6)) +
scale_y_continuous(labels = dollar)
Code along 3
Modify your code from code along 2 to zoom in on the y-axis to see
home prices ranging from 0 to $1,000,000, and zoom in on
the x-axis to view homes on lots ranging in size from 0 to 5 acres. Also
use the dollar function to format the home prices on the y-axis.
ggplot(homes) +
aes(x = LotSize, y = TotalValue) +
geom_point() +
geom_smooth() +
facet_wrap(~ HSDistrict) +
coord_cartesian(xlim = c(0, 5),
ylim = c(0, 1e6)) +
scale_y_continuous(labels = dollar)
`geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

Visualizing counts
Recall the condition variable. We can quickly get counts of each
condition using the count() function from the dplyr
package:
homes %>% count(Condition)
One way to visualize counts is with a bar plot. We can create a bar
plot in ggplot2 with geom_bar(). Notice it automatically
generates the counts for us.

One of the aesthetics of bars is their “fill” color. We can map a
variable from our data frame to the fill aesthetic. For example, let’s
see counts of conditions for homes with and without central air
(Cooling).

The default result is to stack the bars. We can set them side-by-side
setting the position argument to “dodge”.

The large number of Average homes makes it difficult to see the
counts for the other categories. We could use
coord_cartesian() to zoom in on the y-axis. An alternative
approach is to view the proportion of homes with and without central air
within each condition. We can do this by setting the position argument
to “fill”.

The y-axis indicates proportions instead of counts.
Code along 4
Given a home’s condition, is it more likely to be in a particular
high school district? For example, given all the homes in Excellent
condition, is there a higher proportion of them in one of the high
school districts?
Create a bar plot of Condition (Condition on x-axis), filled by
HSDistrict. Set position = ‘fill’ so we visualize within each Condition
the proportion belonging to a high school district.
Reminder: Insert a new code chunk by pressing Ctrl+Alt+I
(Win/Linux) or Cmd+Option+I (Mac).
ggplot(homes) +
aes(x = Condition, fill = HSDistrict) +
geom_bar(position = "fill")

Numeric values vs groups
How does the TotalValue of homes differ between elementary school
districts? One way to visualize this is with a boxplot. We can do this
with geom_boxplot.
ggplot(homes) +
aes(x = ESDistrict, y = TotalValue) +
geom_boxplot()

The x-axis is hard to read. This happens sometimes. One solution is
to simply rotate the plot. We can do that with the
coord_flip() function. The coord_flip()
function also allows us to zoom in on a plot with the xlim and ylim
arguments. Let’s zoom in on the y-axis and look at homes less than 2
million in TotalValue.
ggplot(homes) +
aes(x = ESDistrict, y = TotalValue) +
geom_boxplot() +
coord_flip(ylim = c(0,2e6))

Quick review of boxplots:
- The width of the box (along the y axis) represents the middle 50% of
the data. It is called the Interquartile Range (IQR).
- The line in the box is the median.
- The top of the box is the 75th percentile.
- The bottom of the box is the 25th percentile.
- The “whiskers” extending out of the box are to the highest and
lowest values not to exceed 1.5 x IQR
- The dots are considered “outliers” relative to the rest of the
data.
A similar plot is the violin plot, which allows us to compare the
distributions between categories using smooth density plots. Use
geom_violin()
ggplot(homes) +
aes(x = ESDistrict, y = TotalValue) +
geom_violin() +
coord_flip(ylim = c(0,2e6))

Boxplots and violin plots obscure the total number within in each
group. That’s not necessarily a bad thing. Too much information in a
plot can be overwhelming. However we could create a 1-dimensional
scatterplot, sometimes called a “stripchart”. The
geom_jitter() function is like geom_point()
except it lets you add some random noise to “jitter” the points.

Code along 5
How are the ages of homes distributed between the elementary school
districts? Are there some elementary schools that serve mostly newer
homes? Create a boxplot of Age of home by ESDistrict.
Reminder: Insert a new code chunk by pressing Ctrl+Alt+I
(Win/Linux) or Cmd+Option+I (Mac).
ggplot(homes) +
aes(x = ESDistrict, y = Age) +
geom_boxplot() +
coord_flip()

Line plots over time
How many homes have been built each year in Albemarle county? What
year saw the most homes built? We can use the YearBuilt variable in our
data to help answer this. We just need to count up the number of homes
per YearBuilt and save the result as a data frame. We’ll use what we
learned in the last session to do this.
homes_year <- homes %>%
count(YearBuilt)
tail(homes_year, n = 10)
Let’s plot n vs YearBuilt using a line.
ggplot(homes_year) +
aes(x = YearBuilt, y = n) +
geom_line()

There was a boom sometime after 1750. Which year was it? Around 1950
it seems the number of homes built per year really started to take off.
When was the peak? It also looks like we saw a dip sometime after 2000.
What year was that?
The above plot shows some interesting trends and events, but it’s
hard to get precise years and numbers. Fortunately there are ways to
make ggplot graphs interactive. Here’s one way using the plotly package.
plotly is package that allows you to make interactive web graphics.
Simply call ggplotly() immediately after your ggplot
code:
Load plotly…
# install.packages("plotly")
library(plotly)
Create plot as usual with ggplot2…

Call ggplotly() to create an interactive plot
Now we can interact with the plot to see information of interest.
Hovering over points reveals precise information. We can also zoom in on
portions of the plot. (Double-click in the plotting region to return to
the full plot.)
We can also save our plot and call ggplotly() on it.
p <- ggplot(homes_year) +
aes(x = YearBuilt, y = n) +
geom_line()
ggplotly(p)
Code along 6
How has the mean number of FullBaths in a home changed over time
(YearBuilt), since 1950?
The following code creates a data frame of mean FullBath by YearBuilt
for 1950 to present, but only homes that have not been remodeled.
homes_mean_fb <- homes %>%
filter(YearBuilt >= 1950 & Remodeled == 0) %>%
group_by(YearBuilt) %>%
summarize(FullBath = mean(FullBath))
head(homes_mean_fb)
Create a line plot showing how the mean number of FullBaths has
changed over time (YearBuilt). Try using ggplotly().
Reminder: Insert a new code chunk by pressing Ctrl+Alt+I
(Win/Linux) or Cmd+Option+I (Mac).
ggplot(homes_mean_fb) +
aes(x = YearBuilt, y = FullBath) +
geom_line()

ggplotly()
A package that extends ggplot2: ggridges
There are many packages that extend ggplot, mainly by adding
additional geoms or themes. One that is useful for our data is the
ggridges package.
First notice the challenge of making comparisons of density
histograms between 20 census tracts. (Census tracts are small
subdivisions of a county that are used by the USA Census Bureau for
statistical reporting.)
ggplot(homes) +
aes(x = TotalValue) +
geom_density() +
facet_wrap(~CensusTract) +
coord_cartesian(xlim = c(0, 1e6))

The ggridges package allows us to create ridgeline plots, which are
density plots arranged in a staggered fashion. Notice the new geom
geom_density_ridges().
# install.packages("ggridges")
library(ggridges)
ggplot(homes) +
aes(x = TotalValue, y = CensusTract) +
geom_density_ridges() +
coord_cartesian(xlim = c(0, 1e6))
Picking joint bandwidth of 41400

The ggridges package provides a custom theme specifically for use
with ridgeline plots. Just tack on the function
theme_ridges() to use it.
ggplot(homes) +
aes(x = TotalValue, y = CensusTract) +
geom_density_ridges() +
coord_cartesian(xlim = c(0, 1e6)) +
theme_ridges()
Picking joint bandwidth of 41400

We can reorder the levels of CensusTract by a summary statistic such
as the median of TotalValue. Notice we use the base R function
reorder to accomplish this. That will produce a ridgeline
plot where the distributions are ordered by median TotalValue of each
census tract. Below we use the labs function to add a title
and update the axis labels.
ggplot(homes) +
aes(x = TotalValue, y = reorder(CensusTract, TotalValue, median)) +
geom_density_ridges() +
coord_cartesian(xlim = c(0, 1e6)) +
scale_x_continuous(labels = scales::dollar) +
labs(y = "Census Tract", x = "Total Value",
title = "Distribution of Total Value by Census Tract") +
theme_ridges()
Picking joint bandwidth of 41400

NOTE: ggplotly does not work with plots made by
ggridges.
Creating interactive maps in R with leaflet
When we talk about real estate and census tracts, it’s natural to
want to see visualizations that incorporate maps. Where is census tract
110 in Albemarle county? How big is it geographically?
To create maps we usually need latitude and longitude values so we
can draw shapes, like the borders of counties, states and census tracts.
Our data doesn’t have that information so we need to get it. Fortunately
there is an R package that helps us quickly get this data. The tigris
package allows us to download TIGER/Line shapefiles from the United
States Census Bureau and load into R as “sf” objects. SF stands for
“simple features” which is a standardized way of encoding spatial vector
data in computers.
I already downloaded the census tract borders and saved as an rds
file for us to use to help save time. The code I used is below,
commented out. The final uncommented line reads in the data.
# install.packages("tigris")
# library(tigris)
#
# this is going to download about 15 MB of data; may take a second
# alb <- tracts(state = "VA", county = "Albemarle", year = 2019)
# format the internal latitude and longitude values as numeric. These represent
# the geographic center of each census tract.
# alb <- alb %>%
# mutate(INTPTLON = as.numeric(INTPTLON),
# INTPTLAT = as.numeric(INTPTLAT)) %>%
# sf::st_transform(crs = '+proj=longlat +datum=WGS84')
# saveRDS(alb, file = "../data/alb.Rds")
alb <- readRDS(url("https://github.com/uvastatlab/DJA/raw/main/data/alb.Rds"))
Now that we have spatial data, we’re ready to create a map. For this
we’ll use the leaflet package. The leaflet package takes a spatial data
frame and allows you to layer on “Tiles” and “Polygons” (among much
else). Tiles are base maps. The default is OpenStreetMap. Polygons are
shapes drawn with the latitude and longitude coordinates stored in the
geometry column of the alb data frame. In this
case it draws the census tract boundaries. The code below produces an
interactive map you can zoom in and move around.
library(leaflet)
leaflet(alb) %>%
addTiles() %>%
addPolygons()
That’s nice but it would help if the census tracts were labeled. We
can do that with addLabelOnlyMarkers(). We need to map the
NAME column of the alb data frame to the label
argument. Set lng = INTPTLON and
lat = INTPTLAT to place labels at the geographic center of
the census tracts. The leaflet package requires we place a tilde
(~) in front of variable names to indicate they’re located
in the data frame called in the leaflet function. Again the
map that is rendered is interactive.
leaflet(alb) %>%
addTiles() %>%
addPolygons() %>%
addLabelOnlyMarkers(lng = ~INTPTLON, lat = ~INTPTLAT, label = ~NAME,
labelOptions = labelOptions(noHide = TRUE))
NA
If we like we can shade the census tract regions according to a
summary statistic such as median total value of a home. Let’s do that!
First we calculate median TotalValue by census tract, and then join with
the alb data frame.
med_tv <- homes %>%
group_by(CensusTract) %>%
summarize(TotalValue = median(TotalValue))
alb <- left_join(alb, med_tv, by = c("NAME" = "CensusTract"))
To shade the census tract regions we once again use
addPolygons(). This time we assign the label
argument to TotalValue to display the median home value when we hover
over a census tract. We also add a legend using
addLegend(). For both of these we use a color palette
function that we create with the colorNumeric() function.
That generates a color palette based on the numeric data we give it.
# create color palette for shading census tracts
pal <- colorNumeric("YlOrRd", alb$TotalValue)
leaflet(alb_med_tv) %>%
addTiles() %>%
addPolygons() %>%
addLabelOnlyMarkers(lng = ~INTPTLON, lat = ~INTPTLAT, label = ~NAME,
labelOptions = labelOptions(noHide = TRUE)) %>%
addPolygons(stroke = FALSE, fillOpacity = 0.5,
smoothFactor = 0.5,
color = ~pal(TotalValue),
label = ~TotalValue) %>%
addLegend(pal = pal,
values = ~TotalValue,
opacity = 0.9,
title = "Median Home Value",
position = "bottomleft")
NA
We’re done!
If you would like to talk more about ggplot2 or anything else
statistics related, I would love to hear from you:
clayford@virginia.edu or
statlab@virginia.edu
References
Wickham, H. (2016), ggplot2: Elegant Graphics for Data
Analysis, 2nd ed, Springer. https://ggplot2-book.org/ (on-line version of
work-in-progress 3rd edition)
Wickham, H. and Grolemund G. (2017), R for Data Science.
O’Reilly. http://r4ds.had.co.nz/
Cheng J, Karambelkar B, Xie Y (2022). leaflet: Create
Interactive Web Maps with the JavaScript ‘Leaflet’ Library. R
package version 2.1.1, https://CRAN.R-project.org/package=leaflet.
C. Sievert. Interactive Web-Based Data Visualization with R,
plotly, and shiny. Chapman and Hall/CRC Florida, 2020. https://plotly-r.com/
LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbiBpbiBSIg0KYXV0aG9yOiAiQ2xheSBGb3JkLCBVVkEgTGlicmFyeSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KDQojIyBRdWljayBJbnRybyB0byBSIE5vdGVib29rcyBhbmQgUiBNYXJrZG93bg0KDQpUaGlzIGlzIGFuIFIgTWFya2Rvd24gTm90ZWJvb2suIEl0IGNvbWJpbmVzIG1hcmtkb3duLCBhIHBsYWluIHRleHQgZm9ybWF0dGluZyBzeW50YXgsIGFuZCBSIGNvZGUuIFdoZW4geW91IGV4ZWN1dGUgUiBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSBvdXRwdXQgYXBwZWFycyBiZW5lYXRoIHRoZSBjb2RlLg0KDQpUaGlzIGZpbGUgd2FzIGNyZWF0ZWQgaW4gUlN0dWRpbyBieSBnb2luZyB0byBGaWxlLi4uTmV3IEZpbGUuLi5SIE5vdGVib29rLg0KDQpSIGNvZGUgbmVlZHMgdG8gYmUgaW4gImNodW5rcyIgZm9yIGl0IHRvIHdvcmsuIEJlbG93IGlzIGFuIGV4YW1wbGUgb2YgYW4gUiBjb2RlIGNodW5rLiBJdCBtYWtlcyBhIHBhcmFib2xhIHVzaW5nIGJhc2UgUiBncmFwaGljcyAobm90IGdncGxvdDIpLg0KDQpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciogKFdpbi9MaW51eCkgb3IgKkNtZCtTaGlmdCtSZXR1cm4qIChNYWMpLg0KDQpgYGB7cn0NCnggPC0gc2VxKC0xLCAxLCBieSA9IDAuMDEpDQp5IDwtIHheMg0KcGxvdCh4LCB5LCB0eXBlID0gImwiKQ0KYGBgDQoNClRvIGhpZGUgdGhlIG91dHB1dCwgY2xpY2sgdGhlIEV4cGFuZC9Db2xsYXBzZSBvdXRwdXQgYnV0dG9uLiBUbyBjbGVhciByZXN1bHRzIChvciBhbiBlcnJvciksIGNsaWNrIHRoZSAieCIuDQoNCllvdSBjYW4gYWxzbyBwcmVzcyAqQ3RybCtFbnRlciogKFdpbi9MaW51eCkgb3IgKkNtZCtSZXR1cm4qIChNYWMpIHRvIHJ1biBvbmUgbGluZSBvZiBjb2RlIGF0IGEgdGltZSAoaW5zdGVhZCBvZiB0aGUgZW50aXJlIGNodW5rKS4NCg0KQWRkIGEgbmV3IFIgY29kZSBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKiAoV2luL0xpbnV4KSBvciAqQ21kK09wdGlvbitJKiAoTWFjKS4NCg0KIyMgQ29kZSBhbG9uZyAwDQoNCkluc2VydCBhIG5ldyBSIGNvZGUgY2h1bmsgYmVsb3cgYW5kIHR5cGUgYW5kIHJ1biB0aGUgY29kZTogMjIvNw0KDQoNCiMjIEltcG9ydCBkYXRhDQoNCkxldCdzIGltcG9ydCBvdXIgQWxiZW1hcmxlIENvdW50eSByZWFsIGVzdGF0ZSBkYXRhLiBXZSB1c2UgdGhlIGByZWFkUkRTKClgIGZ1bmN0aW9uLiBSZWNhbGwgdGhhdCBhbiBgcmRzYCBmaWxlIGlzIHNpbXBseSBhbnkgUiBvYmplY3QgdGhhdCdzIGJlZW4gc2F2ZWQuIEluIHRoaXMgY2FzZSB3ZSBzYXZlZCBhIGRhdGEgZnJhbWUuIEJlbG93IHdlIHJlYWQgaW4gdGhlIHJkcyBmaWxlIHZpYSBVUkwuIFRvIGRvIHRoaXMgd2UgbmVlZCB0byB1c2UgdGhlIGJhc2UgUiBgdXJsKClgIGZ1bmN0aW9uIHRvIG9wZW4gYSBjb25uZWN0aW9uIHRvIHRoZSB3ZWJzaXRlIHdoZXJlIHRoZSBmaWxlIGlzIHN0b3JlZC4NCg0KYGBge3J9DQpVUkwgPC0gImh0dHBzOi8vZ2l0aHViLmNvbS91dmFzdGF0bGFiL0RKQS9yYXcvbWFpbi9kYXRhL2FsYmVtYXJsZV9ob21lc18yMDIyLnJkcyINCmhvbWVzIDwtIHJlYWRSRFModXJsKFVSTCkpDQoNCmBgYA0KDQpMZXQncyBsb29rIGF0IHRoZSBmaXJzdCBmZXcgcm93czoNCg0KYGBge3J9DQpoZWFkKGhvbWVzKQ0KYGBgDQoNClZhcmlhYmxlIG5hbWUgZGVmaW5pdGlvbnM6DQoNCi0gICAqWWVhckJ1aWx0KjogeWVhciBob3VzZSB3YXMgYnVpbHQNCi0gICAqWWVhclJlbW9kZWxlZCo6IHllYXIgaG91c2Ugd2FzIHJlbW9kZWxlZA0KLSAgICpDb25kaXRpb24qOiBhc3Nlc3NlZCBjb25kaXRpb24gb2YgaG9tZQ0KLSAgICpGaW5TcUZ0KjogZmluaXNoZWQgc3F1YXJlIGZlZXQNCi0gICAqQ29vbGluZyo6IENlbnRyYWwgQWlyIGluZGljYXRvcg0KLSAgICpGUF9PcGVuKjogbnVtYmVyIG9mIGZpcmVwbGFjZXMNCi0gICAqQmVkcm9vbSo6IG51bWJlciBvZiBiZWRyb29tcw0KLSAgICpGdWxsQmF0aCo6IG51bWJlciBvZiBmdWxsIGJhdGhyb29tcyAodG9pbGV0LCBzaW5rIGFuZCBiYXRoKQ0KLSAgICpIYWxmQmF0aCo6IG51bWJlciBvZiBoYWxmIGJhdGhyb29tcyAodG9pbGV0IGFuZCBzaW5rIG9ubHkpDQotICAgKlRvdGFsUm9vbXMqOiB0b3RhbCByb29tcyBpbiBob3VzZQ0KLSAgICpMb3RTaXplKjogc2l6ZSBvZiBsYW5kIG9uIHdoaWNoIGhvbWUgaXMgbG9jYXRlZCwgaW4gYWNyZXMNCi0gICAqTGFuZFZhbHVlKjogYXNzZXNzZWQgdmFsdWUgb2YgdGhlIGxhbmQNCi0gICAqSW1wcm92ZW1lbnRzVmFsdWUqOiBhc3Nlc3NlZCB2YWx1ZSBvZiAiaW1wcm92ZW1lbnRzIiB0byBsb3QNCi0gICAqVG90YWxWYWx1ZSo6IHRvdGFsIGFzc2Vzc2VkIHZhbHVlIG9mIGhvbWUgYW5kIHByb3BlcnR5DQotICAgKkxhc3RTYWxlUHJpY2UqOiBwcmljZSBvZiBob21lIHdoZW4gbGFzdCBzb2xkDQotICAgKkxhc3RTYWxlRGF0ZSo6IGRhdGUgaG9tZSB3YXMgbGFzdCBzb2xkDQotICAgKkVTZGlzdHJpY3QqOiB0aGUgZWxlbWVudGFyeSBzY2hvb2wgdGhlIGhvbWUgZmVlZHMgaW50bw0KLSAgICpNU2Rpc3RyaWN0KjogdGhlIG1pZGRsZSBzY2hvb2wgdGhlIGhvbWUgZmVlZHMgaW50bw0KLSAgICpIU0Rpc3RyaWN0KjogdGhlIGhpZ2ggc2Nob29sIHRoZSBob21lIGZlZWRzIGludG8NCi0gICAqQ2Vuc3VzVHJhY3QqOiB0aGUgY2Vuc3VzIHRyYWN0IHRoZSBob21lIGlzIGxvY2F0ZWQgaW4NCi0gICAqQWdlKjogb2YgdGhlIGhvdXNlIGluIHllYXJzIGFzIG9mIDIwMjINCi0gICAqUmVtb2RlbGVkKjogaW5kaWNhdG9yIGlmIGhvdXNlIGhhcyBiZWVuIHJlbW9kZWxlZCAoMD1ubywgMT15ZXMpDQotICAgKkZQKjogaW5kaWNhdG9yIGlmIGhvdXNlIGhhcyBmaXJlcGxhY2UgKDA9bm8sIDE9eWVzKQ0KDQojIyBMb2FkIHBhY2thZ2VzIHdlJ2xsIHVzZSB0b2RheQ0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikgDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzY2FsZXMpICMgZm9yIGZvcm1hdHRpbmcgYXhpcyB0aWNrIGxhYmVscw0KYGBgDQoNCg0KIyMgSW50cm8gdG8gZ2dwbG90Mg0KDQpUaGUgZ2dwbG90MiBwYWNrYWdlIGltcGxlbWVudHMgKlRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzKiBhcyBkZWZpbmVkIGluIHRoZSBib29rIG9mIHRoZSBzYW1lIG5hbWUgYnkgTGVsYW5kIFdpbGtpbnNvbi4NCg0KSXQgcmVxdWlyZXMgeW91ciBkYXRhIGJlIGluIGEgZGF0YSBmcmFtZSAob3IgdGliYmxlKS4NCg0KTGV0J3MgZG8gYSBxdWljayBleGFtcGxlLiBIb3cgZG9lcyB0aGUgdG90YWwgdmFsdWUgb2YgYSBob21lIHJlbGF0ZSB0byBpdHMgc2l6ZSBpbiBmaW5pc2hlZCBzcXVhcmUgZmVldD8gUGxvdCBUb3RhbFZhbHVlIHZlcnN1cyBGaW5TcUZ0IHRvIGNyZWF0ZSBhIHNjYXR0ZXIgcGxvdC4NCg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IEZpblNxRnQsIHkgPSBUb3RhbFZhbHVlKSArDQogIGdlb21fcG9pbnQoKSANCmBgYA0KDQpXaGF0IGp1c3QgaGFwcGVuZWQ/DQoNCi0gICBUaGUgYGdncGxvdGAgZnVuY3Rpb24gY3JlYXRlcyBhIHBsb3R0aW5nIGFyZWEgZm9yIGEgZGF0YSBmcmFtZQ0KLSAgIFRoZSBgYWVzYCBmdW5jdGlvbiBtYXBzIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBmcmFtZSB0byBhZXN0aGV0aWMgcHJvcGVydGllcyBvZiBnZW9tZXRyaWMgc2hhcGVzDQotICAgVGhlIGBnZW9tX3BvaW50YCBmdW5jdGlvbiBzcGVjaWZpZXMgd2Ugd2FudCB0byB1c2UgcG9pbnRzDQotICAgY29tYmluZSB0aGVzZSBmdW5jdGlvbnMgd2l0aCBgK2AsIHdoaWNoIGFkZHMgY29tcG9uZW50cyB0byBhIHBsb3QuIFRoZSBgK2AgaXMgbm90IGEgcGlwZSEgSXQgaXMgYW4gb3BlcmF0b3IuDQoNCkRvbid0IGZvcmdldCB0aGUgcGFyZW50aGVzZXMgb24gYGdlb21fcG9pbnQoKWAhDQoNCipNb3N0IG9mIHdoYXQgd2UgZG8gd2l0aCBnZ3Bsb3QyIHRha2VzIHRoaXMgZ2VuZXJhbCBmb3JtKg0KDQpgYGANCmdncGxvdChkYXRhKSArDQogIGFlcygpICsNCiAgZ2VvbV9zb21ldGhpbmcoKQ0KICANCmBgYA0KDQpPciB0aGlzIGZvcm0sIHdpdGggdGhlIGFlc3RoZXRpY3MgZGVmaW5lZCBpbiB0aGUgYGdncGxvdCgpYCBmdW5jdGlvbjoNCg0KYGBgDQpnZ3Bsb3QoZGF0YSwgYWVzKCkpICsNCiAgZ2VvbV9zb21ldGhpbmcoKQ0KDQpgYGANCg0KVGhlcmUgYXJlIG1hbnkgYGdlb21fYCBmdW5jdGlvbnMgaW4gZ2dwbG90Mi4gVGhpcyBuYW1pbmcgY29udmVudGlvbiBtYWtlcyB0aGVtIGVhc2llciB0byBmaW5kIHVzaW5nIFJTdHVkaW8ncyBhdXRvY29tcGxldGUgZnVuY3Rpb25hbGl0eS4gDQogICAgICANCkl0IGNhbiBnZXQgbXVjaCBtb3JlIGNvbXBsaWNhdGVkLCBidXQgZm9yIGV4cGxvcmF0b3J5IHZpc3VhbGl6YXRpb24gdGhpcyBvZnRlbiBzdWZmaWNpZW50Lg0KDQojIyBDb2RlIGFsb25nIDENCg0KRG8geW91bmdlciAobW9yZSByZWNlbnRseSBidWlsdCkgaG91c2VzIGhhdmUgYSB0ZW5kZW5jeSB0byBiZSBiaWdnZXIgdGhhbiBvbGRlciBob3VzZXM/IENyZWF0ZSBhIHNjYXR0ZXJwbG90IG9mIEZpblNxRnQgKHktYXhpcykgdmVyc3VzIEFnZSAoeC1heGlzKS4gDQoNCi0gVHJ5IGVudGVyaW5nIGBhbHBoYSA9IDEvMTBgIGluIGBnZW9tX3BvaW50KClgLiBXaGF0IGRvZXMgaXQgZG8/DQotIFRyeSBlbnRlcmluZyBgc2hhcGUgPSAiLiJgIGluIGBnZW9tX3BvaW50KClgLiBXaGF0IGRvZXMgaXQgZG8/DQotIFRyeSBlbnRlcmluZyBgc2hhcGUgPSAxYCBpbiBgZ2VvbV9wb2ludCgpYC4gV2hhdCBkb2VzIGl0IGRvPyAoc2VlIGA/cG9pbnRzYCBzaGFwZXMgYW5kIHRoZWlyIG51bWVyaWMgY29kZXMpDQoNClJlbWluZGVyOiBJbnNlcnQgYSBuZXcgY29kZSBjaHVuayBieSBwcmVzc2luZyAqQ3RybCtBbHQrSSogKFdpbi9MaW51eCkgb3IgKkNtZCtPcHRpb24rSSogKE1hYykuIA0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IEFnZSwgeSA9IEZpblNxRnQpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApDQpgYGANCg0KDQoNCiMjIE1vcmUgYWVzdGhldGljcyBhbmQgZ2VvbXMsIGFuZCBpbnRyb2R1Y2luZyBmYWNldHMNCg0KUG9pbnRzIG5vdCBvbmx5IGhhdmUgcG9zaXRpb25zIGluIGEgcGxhbmUsIGJ1dCBjYW4gYmUgZGlmZmVyZW50IHNpemVzIGFuZCBoYXZlIGRpZmZlcmVudCBzaGFwZXMgYW5kIGNvbG9ycy4gV2UgY2FuICJtYXAiIHZhcmlhYmxlcyBpbiBvdXIgZGF0YSBmcmFtZSB0byB0aGVzZSBhZXN0aGV0aWNzLiBJbiBvdGhlciB3b3Jkcywgd2UgbGV0IHZhcmlhYmxlcyBkZWZpbmUgcHJvcGVydGllcyBvZiBwb2ludHMgc3VjaCBhcyBzaXplLCBzaGFwZSwgYW5kIGNvbG9yLg0KDQpIb3cgZG9lcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gVG90YWxWYWx1ZSBhbmQgRmluU3FGdCBkaWZmZXIgYmV0d2VlbiBob21lcyB3aXRoIGFuZCB3aXRob3V0IGNlbnRyYWwgYWlyPw0KDQpQbG90IFRvdGFsVmFsdWUgdmVyc3VzIEZpblNxRnQgdG8gY3JlYXRlIGEgc2NhdHRlciBwbG90LCBidXQgYWxzbyBjb2xvciB0aGUgcG9pbnRzIGFjY29yZGluZyB0byB3aGV0aGVyIG9yIG5vdCB0aGUgaG9tZXMgaGF2ZSBjZW50cmFsIGFpciAoQ29vbGluZykgdXNpbmcgdGhlIGBjb2xvcmAgYXJndW1lbnQuIA0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IEZpblNxRnQsIHkgPSBUb3RhbFZhbHVlLCBjb2xvciA9IENvb2xpbmcpICsNCiAgZ2VvbV9wb2ludCgpIA0KYGBgDQoNCldlIGNhbiBhbHNvIG1hcCB0aGUgc2l6ZSBvZiB0aGUgcG9pbnRzIHRvIGEgdmFyaWFibGUsIHN1Y2ggYXMgTG90U2l6ZSwgdXNpbmcgdGhlIGBzaXplYCBhcmd1bWVudC4gTm90aWNlIEkgaGF2ZSBhbHNvIG1hcHBlZCB0aGUgc2hhcGUgb2YgdGhlIHBvaW50IHRvIENvb2xpbmcgdXNpbmcgdGhlIGBzaGFwZWAgYXJndW1lbnQuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gRmluU3FGdCwgeSA9IFRvdGFsVmFsdWUsIA0KICAgICAgc2hhcGUgPSBDb29saW5nLCBzaXplID0gTG90U2l6ZSkgKw0KICBnZW9tX3BvaW50KCkgDQoNCmBgYA0KDQpOb3RpY2UgdGhlIG92ZXJwbG90dGluZy4gUGVyaGFwcyB3ZSBwcmVmZXIgdG8gc2VlIHNlcGFyYXRlIHNjYXR0ZXJwbG90cyBmb3IgZWFjaCBDb29saW5nIGNhdGVnb3J5LiBXZSBjYW4gYWNoaWV2ZSB0aGF0IHdpdGggKmZhY2V0cyouIFRoZSBmdW5jdGlvbiB0byB1c2UgaXMgYGZhY2V0X3dyYXAoKWAgd2l0aCB0aGUgdmFyaWFibGUgdGhhdCB3ZSB3YW50IHRvIGZhY2V0IG9uLiBJbiB0aGlzIGNhc2UgaXQncyBDb29saW5nLiBOb3RpY2Ugd2UgcHJlY2VkZSB0aGUgdmFyaWFibGUgd2l0aCBhIHRpbGRlIGB+YC4gV2UgY2FuIHJlYWQgdGhhdCBhcyAiZmFjZXQgYnkgQ29vbGluZyIuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gRmluU3FGdCwgeSA9IFRvdGFsVmFsdWUsIHNpemUgPSBMb3RTaXplKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAxLzIpICsNCiAgZmFjZXRfd3JhcCh+IENvb2xpbmcpDQoNCmBgYA0KDQoNCldlIGNhbiBhbHNvIGhhdmUgbXVsdGlwbGUgZ2VvbXMgaW4gYSBwbG90LiBMZXQncyBzYXkgd2Ugd2FudCB0byBhZGQgYSBzbW9vdGggdHJlbmQgbGluZSB0aGF0IHN1bW1hcml6ZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEZpblNxRnQgYW5kIFRvdGFsVmFsdWUuIEFkZCB0aGUgYGdlb21fc21vb3RoKClgDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gRmluU3FGdCwgeSA9IFRvdGFsVmFsdWUpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDEvMikgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh+IENvb2xpbmcpIA0KDQpgYGANCg0KIyMgQ29kZSBhbG9uZyAyDQoNCkhvdyBpcyBUb3RhbFZhbHVlIHJlbGF0ZWQgdG8gTG90U2l6ZSB3aXRoaW4gZWFjaCBoaWdoIHNjaG9vbCBkaXN0cmljdD8NCg0KQ3JlYXRlIGEgc2NhdHRlcnBsb3Qgb2YgVG90YWxWYWx1ZSAoeS1heGlzKSB2cyBMb3RTaXplICh4LWF4aXMpLCBmYWNldGVkIGJ5IEhTRGlzdHJpY3QuIEFkZCBzbW9vdGggdHJlbmQgbGluZXMgdG8gc3VtbWFyaXplIHRoZSByZWxhdGlvbnNoaXAuDQoNClJlbWluZGVyOiBJbnNlcnQgYSBuZXcgY29kZSBjaHVuayBieSBwcmVzc2luZyAqQ3RybCtBbHQrSSogKFdpbi9MaW51eCkgb3IgKkNtZCtPcHRpb24rSSogKE1hYykuIA0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IExvdFNpemUsIHkgPSBUb3RhbFZhbHVlKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBmYWNldF93cmFwKH5IU0Rpc3RyaWN0KQ0KYGBgDQoNCg0KDQojIyBCaW5uaW5nIGFuZCB6b29taW5nDQoNCkRlYWxpbmcgd2l0aCBsYXJnZSBhbW91bnRzIHRvIGRhdGEgbGVhZHMgdG8gb3ZlcnBsb3R0aW5nLiBTbyBmYXIgd2UndmUgdXNlZCBhbHBoYSB2YWx1ZXMgdG8gbW9kaWZ5IHRyYW5zcGFyZW5jeSBmcm9tIDAgKGludmlzaWJsZSkgdG8gMSAoc29saWQpLCBmYWNldHMsIGFuZCBkaWZmZXJlbnQgZ2VvbWV0cmljIHNoYXBlcyB0byBoZWxwIGFsbGV2aWF0ZSB0aGlzLiBBbm90aGVyIGlzICJiaW5uaW5nIi4gQXMgbG9uZyBhcyB5b3UgaGF2ZSB0aGUgYGhleGJpbmAgcGFja2FnZSBpbnN0YWxsZWQsIHRoaXMgY2FuIGJlIGVhc2lseSBhY2NvbXBsaXNoZWQgd2l0aCBgZ2VvbV9oZXhgLiBUaGlzICJiaW5zIiBjb3VudHMgb2YgcG9pbnRzIGludG8gaGV4YWdvbnMgYW5kIGNvbG9ycyB0aGUgaGV4YWdvbnMgYWNjb3JkaW5nIHRvIGNvdW50cy4gTGlnaHRlciBjb2xvcnMgbWVhbiBoaWdoZXIgY291bnRzLiBDaGFuZ2luZyB0aGUgbnVtYmVyIG9mIGJpbnMgKGRlZmF1bHQgMzAgeCAzMCkgZGVjcmVhc2VzL2luY3JlYXNlcyB0aGUgbnVtYmVyIG9mIGhleGFnb25zLiBGb3IgZXhhbXBsZSwgYGJpbnMgPSA0MGANCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImhleGJpbiIpDQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBGaW5TcUZ0LCB5ID0gVG90YWxWYWx1ZSkgKw0KICBnZW9tX2hleChiaW5zID0gNDApIA0KDQpgYGANCg0KDQpBbGJlbWFybGUgY291bnR5IGhhcyBob21lcyB3aXRoIHZlcnkgbGFyZ2UgVG90YWxWYWx1ZSBhbmQgTG90U2l6ZSB2YWx1ZXMuIEluY2x1ZGluZyB0aGVtIGluIG91ciBwbG90cyBtZWFucyB3ZSAiem9vbSBvdXQiIHRvIGFjY29tbW9kYXRlIHRoZW0gd2hpY2ggbWVhbnMgd2UncmUgImZhciBhd2F5IiBmcm9tIHRoZSBtYWpvcml0eSBvZiBob21lcy4NCg0KVG8gem9vbSBpbiBvbiBkYXRhIHdpdGggZ2dwbG90Miwgd2UgdXNlIHRoZSBgY29vcmRfY2FydGVzaW9uKClgIGZ1bmN0aW9uIGFuZCBkZWZpbmUgdGhlIGxpbWl0cyB0byB2aWV3IHdpdGggdGhlIGB4bGltYCBhbmQvb3IgYHlsaW1gIGFyZ3VtZW50cy4gDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gRmluU3FGdCwgeSA9IFRvdGFsVmFsdWUpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgoKSArIA0KICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNTAwMCksICMgZnJvbSAwIHRvIDUsMDAwDQogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCAyZTYpKSAjIGZyb20gMCB0byAkMiwwMDAsMDAwDQoNCmBgYA0KDQpJZiBwcmVzZW50aW5nIHRoaXMgcGxvdCB0byBhbiBhdWRpZW5jZSwgdGVsbCB0aGVtIGl0J3Mgem9vbWVkIGluIGFuZCBsb29raW5nIGF0IGEgc3Vic2V0IG9mIHlvdXIgZGF0YS4NCg0KVGhlIG51bWJlcnMgYXJlIGEgbGl0dGxlIGhhcmQgdG8gcmVhZCBvbiB0aGUgeS1heGlzLiBXZSBjYW4gZm9ybWF0IHRoZW0gYXMgZG9sbGFyIGFtb3VudHMgdXNpbmcgdGhlIGxhYmVscyBhcmd1bWVudCBpbiB0aGUgYHNjYWxlX3lfY29udGludW91cygpYCBmdW5jdGlvbi4gVG8gZG8gdGhpcyB3ZSBjYW4gdXNlIHRoZSBgZG9sbGFyYCBmdW5jdGlvbiBmcm9tIHRoZSBzY2FsZXMgcGFja2FnZSwgd2hpY2ggaXMgaW5zdGFsbGVkIHdoZW4geW91IGluc3RhbGwgdGhlIGdncGxvdDIgcGFja2FnZS4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBGaW5TcUZ0LCB5ID0gVG90YWxWYWx1ZSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDUwMDApLA0KICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMmU2KSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcikgDQoNCmBgYA0KDQojIyBDb2RlIGFsb25nIDMNCg0KTW9kaWZ5IHlvdXIgY29kZSBmcm9tIGNvZGUgYWxvbmcgMiB0byB6b29tIGluIG9uIHRoZSB5LWF4aXMgdG8gc2VlIGhvbWUgcHJpY2VzIHJhbmdpbmcgZnJvbSAwIHRvIGAkMSwwMDAsMDAwYCwgYW5kIHpvb20gaW4gb24gdGhlIHgtYXhpcyB0byB2aWV3IGhvbWVzIG9uIGxvdHMgcmFuZ2luZyBpbiBzaXplIGZyb20gMCB0byA1IGFjcmVzLiBBbHNvIHVzZSB0aGUgZG9sbGFyIGZ1bmN0aW9uIHRvIGZvcm1hdCB0aGUgaG9tZSBwcmljZXMgb24gdGhlIHktYXhpcy4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBMb3RTaXplLCB5ID0gVG90YWxWYWx1ZSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh+IEhTRGlzdHJpY3QpICsNCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDUpLA0KICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMWU2KSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gZG9sbGFyKQ0KDQpgYGANCg0KIyMgVmlzdWFsaXppbmcgY291bnRzDQoNClJlY2FsbCB0aGUgY29uZGl0aW9uIHZhcmlhYmxlLiBXZSBjYW4gcXVpY2tseSBnZXQgY291bnRzIG9mIGVhY2ggY29uZGl0aW9uIHVzaW5nIHRoZSBgY291bnQoKWAgZnVuY3Rpb24gZnJvbSB0aGUgZHBseXIgcGFja2FnZToNCg0KYGBge3J9DQpob21lcyAlPiUgY291bnQoQ29uZGl0aW9uKQ0KYGBgDQoNCk9uZSB3YXkgdG8gdmlzdWFsaXplIGNvdW50cyBpcyB3aXRoIGEgYmFyIHBsb3QuIFdlIGNhbiBjcmVhdGUgYSBiYXIgcGxvdCBpbiBnZ3Bsb3QyIHdpdGggYGdlb21fYmFyKClgLiBOb3RpY2UgaXQgYXV0b21hdGljYWxseSBnZW5lcmF0ZXMgdGhlIGNvdW50cyBmb3IgdXMuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gQ29uZGl0aW9uKSArDQogIGdlb21fYmFyKCkNCmBgYA0KDQpPbmUgb2YgdGhlIGFlc3RoZXRpY3Mgb2YgYmFycyBpcyB0aGVpciAiZmlsbCIgY29sb3IuIFdlIGNhbiBtYXAgYSB2YXJpYWJsZSBmcm9tIG91ciBkYXRhIGZyYW1lIHRvIHRoZSBmaWxsIGFlc3RoZXRpYy4gRm9yIGV4YW1wbGUsIGxldCdzIHNlZSBjb3VudHMgb2YgY29uZGl0aW9ucyBmb3IgaG9tZXMgd2l0aCBhbmQgd2l0aG91dCBjZW50cmFsIGFpciAoQ29vbGluZykuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gQ29uZGl0aW9uLCBmaWxsID0gQ29vbGluZykgKw0KICBnZW9tX2JhcigpDQoNCmBgYA0KDQpUaGUgZGVmYXVsdCByZXN1bHQgaXMgdG8gc3RhY2sgdGhlIGJhcnMuIFdlIGNhbiBzZXQgdGhlbSBzaWRlLWJ5LXNpZGUgc2V0dGluZyB0aGUgcG9zaXRpb24gYXJndW1lbnQgdG8gImRvZGdlIi4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBDb25kaXRpb24sIGZpbGwgPSBDb29saW5nKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikgDQoNCmBgYA0KDQpUaGUgbGFyZ2UgbnVtYmVyIG9mIEF2ZXJhZ2UgaG9tZXMgbWFrZXMgaXQgZGlmZmljdWx0IHRvIHNlZSB0aGUgY291bnRzIGZvciB0aGUgb3RoZXIgY2F0ZWdvcmllcy4gV2UgY291bGQgdXNlIGBjb29yZF9jYXJ0ZXNpYW4oKWAgdG8gem9vbSBpbiBvbiB0aGUgeS1heGlzLiBBbiBhbHRlcm5hdGl2ZSBhcHByb2FjaCBpcyB0byB2aWV3IHRoZSBwcm9wb3J0aW9uIG9mIGhvbWVzIHdpdGggYW5kIHdpdGhvdXQgY2VudHJhbCBhaXIgd2l0aGluIGVhY2ggY29uZGl0aW9uLiBXZSBjYW4gZG8gdGhpcyBieSBzZXR0aW5nIHRoZSBwb3NpdGlvbiBhcmd1bWVudCB0byAiZmlsbCIuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gQ29uZGl0aW9uLCBmaWxsID0gQ29vbGluZykgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICB5bGFiKCJwcm9wb3J0aW9uIikNCmBgYA0KDQpUaGUgeS1heGlzIGluZGljYXRlcyBwcm9wb3J0aW9ucyBpbnN0ZWFkIG9mIGNvdW50cy4NCg0KDQojIyBDb2RlIGFsb25nIDQNCg0KR2l2ZW4gYSBob21lJ3MgY29uZGl0aW9uLCBpcyBpdCBtb3JlIGxpa2VseSB0byBiZSBpbiBhIHBhcnRpY3VsYXIgaGlnaCBzY2hvb2wgZGlzdHJpY3Q/IEZvciBleGFtcGxlLCBnaXZlbiBhbGwgdGhlIGhvbWVzIGluIEV4Y2VsbGVudCBjb25kaXRpb24sIGlzIHRoZXJlIGEgaGlnaGVyIHByb3BvcnRpb24gb2YgdGhlbSBpbiBvbmUgb2YgdGhlIGhpZ2ggc2Nob29sIGRpc3RyaWN0cz8NCg0KQ3JlYXRlIGEgYmFyIHBsb3Qgb2YgQ29uZGl0aW9uIChDb25kaXRpb24gb24geC1heGlzKSwgZmlsbGVkIGJ5IEhTRGlzdHJpY3QuIFNldCBwb3NpdGlvbiA9ICdmaWxsJyBzbyB3ZSB2aXN1YWxpemUgd2l0aGluIGVhY2ggQ29uZGl0aW9uIHRoZSBwcm9wb3J0aW9uIGJlbG9uZ2luZyB0byBhIGhpZ2ggc2Nob29sIGRpc3RyaWN0Lg0KDQpSZW1pbmRlcjogSW5zZXJ0IGEgbmV3IGNvZGUgY2h1bmsgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqIChXaW4vTGludXgpIG9yICpDbWQrT3B0aW9uK0kqIChNYWMpLiANCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBDb25kaXRpb24sIGZpbGwgPSBIU0Rpc3RyaWN0KSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKQ0KYGBgDQoNCg0KIyMgTnVtZXJpYyB2YWx1ZXMgdnMgZ3JvdXBzDQoNCkhvdyBkb2VzIHRoZSBUb3RhbFZhbHVlIG9mIGhvbWVzIGRpZmZlciBiZXR3ZWVuIGVsZW1lbnRhcnkgc2Nob29sIGRpc3RyaWN0cz8gT25lIHdheSB0byB2aXN1YWxpemUgdGhpcyBpcyB3aXRoIGEgYm94cGxvdC4gV2UgY2FuIGRvIHRoaXMgd2l0aCBgZ2VvbV9ib3hwbG90YC4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBFU0Rpc3RyaWN0LCB5ID0gVG90YWxWYWx1ZSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQoNClRoZSB4LWF4aXMgaXMgaGFyZCB0byByZWFkLiBUaGlzIGhhcHBlbnMgc29tZXRpbWVzLiBPbmUgc29sdXRpb24gaXMgdG8gc2ltcGx5IHJvdGF0ZSB0aGUgcGxvdC4gV2UgY2FuIGRvIHRoYXQgd2l0aCB0aGUgYGNvb3JkX2ZsaXAoKWAgZnVuY3Rpb24uIFRoZSBgY29vcmRfZmxpcCgpYCBmdW5jdGlvbiBhbHNvIGFsbG93cyB1cyB0byB6b29tIGluIG9uIGEgcGxvdCB3aXRoIHRoZSB4bGltIGFuZCB5bGltIGFyZ3VtZW50cy4gTGV0J3Mgem9vbSBpbiBvbiB0aGUgeS1heGlzIGFuZCBsb29rIGF0IGhvbWVzIGxlc3MgdGhhbiAyIG1pbGxpb24gaW4gVG90YWxWYWx1ZS4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBFU0Rpc3RyaWN0LCB5ID0gVG90YWxWYWx1ZSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGNvb3JkX2ZsaXAoeWxpbSA9IGMoMCwyZTYpKQ0KDQpgYGANCg0KUXVpY2sgcmV2aWV3IG9mIGJveHBsb3RzOg0KDQotICAgVGhlIHdpZHRoIG9mIHRoZSBib3ggKGFsb25nIHRoZSB5IGF4aXMpIHJlcHJlc2VudHMgdGhlIA0KICAgIG1pZGRsZSA1MCUgb2YgdGhlIGRhdGEuIEl0IGlzIGNhbGxlZCB0aGUgSW50ZXJxdWFydGlsZSBSYW5nZSAoSVFSKS4NCi0gICBUaGUgbGluZSBpbiB0aGUgYm94IGlzIHRoZSBtZWRpYW4uDQotICAgVGhlIHRvcCBvZiB0aGUgYm94IGlzIHRoZSA3NXRoIHBlcmNlbnRpbGUuDQotICAgVGhlIGJvdHRvbSBvZiB0aGUgYm94IGlzIHRoZSAyNXRoIHBlcmNlbnRpbGUuDQotICAgVGhlICJ3aGlza2VycyIgZXh0ZW5kaW5nIG91dCBvZiB0aGUgYm94IGFyZSB0byB0aGUgaGlnaGVzdCBhbmQgDQogICAgbG93ZXN0IHZhbHVlcyBub3QgdG8gZXhjZWVkIDEuNSB4IElRUg0KLSAgIFRoZSBkb3RzIGFyZSBjb25zaWRlcmVkICJvdXRsaWVycyIgcmVsYXRpdmUgdG8gdGhlIHJlc3Qgb2YgdGhlIGRhdGEuDQoNCkEgc2ltaWxhciBwbG90IGlzIHRoZSB2aW9saW4gcGxvdCwgd2hpY2ggYWxsb3dzIHVzIHRvIGNvbXBhcmUgdGhlIGRpc3RyaWJ1dGlvbnMgYmV0d2VlbiBjYXRlZ29yaWVzIHVzaW5nIHNtb290aCBkZW5zaXR5IHBsb3RzLiBVc2UgYGdlb21fdmlvbGluKClgDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gRVNEaXN0cmljdCwgeSA9IFRvdGFsVmFsdWUpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGNvb3JkX2ZsaXAoeWxpbSA9IGMoMCwyZTYpKQ0KYGBgDQoNCkJveHBsb3RzIGFuZCB2aW9saW4gcGxvdHMgb2JzY3VyZSB0aGUgdG90YWwgbnVtYmVyIHdpdGhpbiBpbiBlYWNoIGdyb3VwLiBUaGF0J3Mgbm90IG5lY2Vzc2FyaWx5IGEgYmFkIHRoaW5nLiBUb28gbXVjaCBpbmZvcm1hdGlvbiBpbiBhIHBsb3QgY2FuIGJlIG92ZXJ3aGVsbWluZy4gSG93ZXZlciB3ZSBjb3VsZCBjcmVhdGUgYSAxLWRpbWVuc2lvbmFsIHNjYXR0ZXJwbG90LCBzb21ldGltZXMgY2FsbGVkIGEgInN0cmlwY2hhcnQiLiBUaGUgYGdlb21faml0dGVyKClgIGZ1bmN0aW9uIGlzIGxpa2UgYGdlb21fcG9pbnQoKWAgZXhjZXB0IGl0IGxldHMgeW91IGFkZCBzb21lIHJhbmRvbSBub2lzZSB0byAiaml0dGVyIiB0aGUgcG9pbnRzLg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IEVTRGlzdHJpY3QsIHkgPSBUb3RhbFZhbHVlKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLCBoZWlnaHQgPSAwLCBzaGFwZSA9ICIuIikgKw0KICBjb29yZF9mbGlwKHlsaW0gPSBjKDAsMmU2KSkNCg0KYGBgDQoNCiMjIENvZGUgYWxvbmcgNQ0KDQpIb3cgYXJlIHRoZSBhZ2VzIG9mIGhvbWVzIGRpc3RyaWJ1dGVkIGJldHdlZW4gdGhlIGVsZW1lbnRhcnkgc2Nob29sIGRpc3RyaWN0cz8gQXJlIHRoZXJlIHNvbWUgZWxlbWVudGFyeSBzY2hvb2xzIHRoYXQgc2VydmUgbW9zdGx5IG5ld2VyIGhvbWVzPyBDcmVhdGUgYSBib3hwbG90IG9mIEFnZSBvZiBob21lIGJ5IEVTRGlzdHJpY3QuDQoNClJlbWluZGVyOiBJbnNlcnQgYSBuZXcgY29kZSBjaHVuayBieSBwcmVzc2luZyAqQ3RybCtBbHQrSSogKFdpbi9MaW51eCkgb3IgKkNtZCtPcHRpb24rSSogKE1hYykuIA0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IEVTRGlzdHJpY3QsIHkgPSBBZ2UpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQoNCg0KDQojIyBMaW5lIHBsb3RzIG92ZXIgdGltZQ0KDQpIb3cgbWFueSBob21lcyBoYXZlIGJlZW4gYnVpbHQgZWFjaCB5ZWFyIGluIEFsYmVtYXJsZSBjb3VudHk/IFdoYXQgeWVhciBzYXcgdGhlIG1vc3QgaG9tZXMgYnVpbHQ/IFdlIGNhbiB1c2UgdGhlIFllYXJCdWlsdCB2YXJpYWJsZSBpbiBvdXIgZGF0YSB0byBoZWxwIGFuc3dlciB0aGlzLiBXZSBqdXN0IG5lZWQgdG8gY291bnQgdXAgdGhlIG51bWJlciBvZiBob21lcyBwZXIgWWVhckJ1aWx0IGFuZCBzYXZlIHRoZSByZXN1bHQgYXMgYSBkYXRhIGZyYW1lLiBXZSdsbCB1c2Ugd2hhdCB3ZSBsZWFybmVkIGluIHRoZSBsYXN0IHNlc3Npb24gdG8gZG8gdGhpcy4gDQoNCmBgYHtyfQ0KaG9tZXNfeWVhciA8LSBob21lcyAlPiUgIA0KICBjb3VudChZZWFyQnVpbHQpIA0KdGFpbChob21lc195ZWFyLCBuID0gMTApDQpgYGANCg0KTGV0J3MgcGxvdCBuIHZzIFllYXJCdWlsdCB1c2luZyBhIGxpbmUuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzX3llYXIpICsNCiAgYWVzKHggPSBZZWFyQnVpbHQsIHkgPSBuKSArDQogIGdlb21fbGluZSgpDQpgYGANCg0KVGhlcmUgd2FzIGEgYm9vbSBzb21ldGltZSBhZnRlciAxNzUwLiBXaGljaCB5ZWFyIHdhcyBpdD8gQXJvdW5kIDE5NTAgaXQgc2VlbXMgdGhlIG51bWJlciBvZiBob21lcyBidWlsdCBwZXIgeWVhciByZWFsbHkgc3RhcnRlZCB0byB0YWtlIG9mZi4gV2hlbiB3YXMgdGhlIHBlYWs/IEl0IGFsc28gbG9va3MgbGlrZSB3ZSBzYXcgYSBkaXAgc29tZXRpbWUgYWZ0ZXIgMjAwMC4gV2hhdCB5ZWFyIHdhcyB0aGF0Pw0KDQpUaGUgYWJvdmUgcGxvdCBzaG93cyBzb21lIGludGVyZXN0aW5nIHRyZW5kcyBhbmQgZXZlbnRzLCBidXQgaXQncyBoYXJkIHRvIGdldCBwcmVjaXNlIHllYXJzIGFuZCBudW1iZXJzLiBGb3J0dW5hdGVseSB0aGVyZSBhcmUgd2F5cyB0byBtYWtlIGdncGxvdCBncmFwaHMgaW50ZXJhY3RpdmUuIEhlcmUncyBvbmUgd2F5IHVzaW5nIHRoZSBwbG90bHkgcGFja2FnZS4gcGxvdGx5IGlzIHBhY2thZ2UgdGhhdCBhbGxvd3MgeW91IHRvIG1ha2UgaW50ZXJhY3RpdmUgd2ViIGdyYXBoaWNzLiBTaW1wbHkgY2FsbCBgZ2dwbG90bHkoKWAgaW1tZWRpYXRlbHkgYWZ0ZXIgeW91ciBnZ3Bsb3QgY29kZToNCg0KTG9hZCBwbG90bHkuLi4NCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQpsaWJyYXJ5KHBsb3RseSkNCmBgYA0KDQpDcmVhdGUgcGxvdCBhcyB1c3VhbCB3aXRoIGdncGxvdDIuLi4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXNfeWVhcikgKw0KICBhZXMoeCA9IFllYXJCdWlsdCwgeSA9IG4pICsNCiAgZ2VvbV9saW5lKCkNCmBgYA0KDQpDYWxsIGBnZ3Bsb3RseSgpYCB0byBjcmVhdGUgYW4gaW50ZXJhY3RpdmUgcGxvdA0KDQpgYGB7cn0NCmdncGxvdGx5KCkNCmBgYA0KDQoNCk5vdyB3ZSBjYW4gaW50ZXJhY3Qgd2l0aCB0aGUgcGxvdCB0byBzZWUgaW5mb3JtYXRpb24gb2YgaW50ZXJlc3QuIEhvdmVyaW5nIG92ZXIgcG9pbnRzIHJldmVhbHMgcHJlY2lzZSBpbmZvcm1hdGlvbi4gV2UgY2FuIGFsc28gem9vbSBpbiBvbiBwb3J0aW9ucyBvZiB0aGUgcGxvdC4gKERvdWJsZS1jbGljayBpbiB0aGUgcGxvdHRpbmcgcmVnaW9uIHRvIHJldHVybiB0byB0aGUgZnVsbCBwbG90LikNCg0KV2UgY2FuIGFsc28gc2F2ZSBvdXIgcGxvdCBhbmQgY2FsbCBgZ2dwbG90bHkoKWAgb24gaXQuDQoNCmBgYHtyfQ0KcCA8LSBnZ3Bsb3QoaG9tZXNfeWVhcikgKw0KICBhZXMoeCA9IFllYXJCdWlsdCwgeSA9IG4pICsNCiAgZ2VvbV9saW5lKCkNCmdncGxvdGx5KHApDQoNCmBgYA0KDQoNCg0KIyMgQ29kZSBhbG9uZyA2DQoNCkhvdyBoYXMgdGhlIG1lYW4gbnVtYmVyIG9mIEZ1bGxCYXRocyBpbiBhIGhvbWUgY2hhbmdlZCBvdmVyIHRpbWUgKFllYXJCdWlsdCksIHNpbmNlIDE5NTA/IA0KDQpUaGUgZm9sbG93aW5nIGNvZGUgY3JlYXRlcyBhIGRhdGEgZnJhbWUgb2YgbWVhbiBGdWxsQmF0aCBieSBZZWFyQnVpbHQgZm9yIDE5NTAgdG8gcHJlc2VudCwgYnV0IG9ubHkgaG9tZXMgdGhhdCBoYXZlIG5vdCBiZWVuIHJlbW9kZWxlZC4NCg0KYGBge3J9DQpob21lc19tZWFuX2ZiIDwtIGhvbWVzICU+JSANCiAgZmlsdGVyKFllYXJCdWlsdCA+PSAxOTUwICYgUmVtb2RlbGVkID09IDApICU+JSANCiAgZ3JvdXBfYnkoWWVhckJ1aWx0KSAlPiUgDQogIHN1bW1hcml6ZShGdWxsQmF0aCA9IG1lYW4oRnVsbEJhdGgpKQ0KICANCmhlYWQoaG9tZXNfbWVhbl9mYikNCmBgYA0KDQpDcmVhdGUgYSBsaW5lIHBsb3Qgc2hvd2luZyBob3cgdGhlIG1lYW4gbnVtYmVyIG9mIEZ1bGxCYXRocyBoYXMgY2hhbmdlZCBvdmVyIHRpbWUgKFllYXJCdWlsdCkuIFRyeSB1c2luZyBgZ2dwbG90bHkoKWAuDQoNClJlbWluZGVyOiBJbnNlcnQgYSBuZXcgY29kZSBjaHVuayBieSBwcmVzc2luZyAqQ3RybCtBbHQrSSogKFdpbi9MaW51eCkgb3IgKkNtZCtPcHRpb24rSSogKE1hYykuIA0KDQpgYGB7cn0NCmdncGxvdChob21lc19tZWFuX2ZiKSArDQogIGFlcyh4ID0gWWVhckJ1aWx0LCB5ID0gRnVsbEJhdGgpICsNCiAgZ2VvbV9saW5lKCkNCmdncGxvdGx5KCkNCmBgYA0KDQoNCiMjIEEgcGFja2FnZSB0aGF0IGV4dGVuZHMgZ2dwbG90MjogZ2dyaWRnZXMNCg0KVGhlcmUgYXJlIG1hbnkgcGFja2FnZXMgdGhhdCBleHRlbmQgZ2dwbG90LCBtYWlubHkgYnkgYWRkaW5nIGFkZGl0aW9uYWwgZ2VvbXMgb3IgdGhlbWVzLiBPbmUgdGhhdCBpcyB1c2VmdWwgZm9yIG91ciBkYXRhIGlzIHRoZSBnZ3JpZGdlcyBwYWNrYWdlLg0KDQpGaXJzdCBub3RpY2UgdGhlIGNoYWxsZW5nZSBvZiBtYWtpbmcgY29tcGFyaXNvbnMgb2YgZGVuc2l0eSBoaXN0b2dyYW1zIGJldHdlZW4gMjAgY2Vuc3VzIHRyYWN0cy4gKENlbnN1cyB0cmFjdHMgYXJlIHNtYWxsIHN1YmRpdmlzaW9ucyBvZiBhIGNvdW50eSB0aGF0IGFyZSB1c2VkIGJ5IHRoZSBVU0EgQ2Vuc3VzIEJ1cmVhdSBmb3Igc3RhdGlzdGljYWwgcmVwb3J0aW5nLikNCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBUb3RhbFZhbHVlKSArDQogIGdlb21fZGVuc2l0eSgpICsNCiAgZmFjZXRfd3JhcCh+Q2Vuc3VzVHJhY3QpICsNCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDFlNikpDQoNCmBgYA0KDQpUaGUgZ2dyaWRnZXMgcGFja2FnZSBhbGxvd3MgdXMgdG8gY3JlYXRlIHJpZGdlbGluZSBwbG90cywgd2hpY2ggYXJlIGRlbnNpdHkgcGxvdHMgYXJyYW5nZWQgaW4gYSBzdGFnZ2VyZWQgZmFzaGlvbi4gTm90aWNlIHRoZSBuZXcgZ2VvbSBgZ2VvbV9kZW5zaXR5X3JpZGdlcygpYC4NCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImdncmlkZ2VzIikNCmxpYnJhcnkoZ2dyaWRnZXMpDQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBUb3RhbFZhbHVlLCB5ID0gQ2Vuc3VzVHJhY3QpICsgDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArDQogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxZTYpKQ0KDQpgYGANCg0KDQpUaGUgZ2dyaWRnZXMgcGFja2FnZSBwcm92aWRlcyBhIGN1c3RvbSB0aGVtZSBzcGVjaWZpY2FsbHkgZm9yIHVzZSB3aXRoIHJpZGdlbGluZSBwbG90cy4gSnVzdCB0YWNrIG9uIHRoZSBmdW5jdGlvbiBgdGhlbWVfcmlkZ2VzKClgIHRvIHVzZSBpdC4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBUb3RhbFZhbHVlLCB5ID0gQ2Vuc3VzVHJhY3QpICsgDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArDQogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxZTYpKSArDQogIHRoZW1lX3JpZGdlcygpDQoNCmBgYA0KDQoNCldlIGNhbiByZW9yZGVyIHRoZSBsZXZlbHMgb2YgQ2Vuc3VzVHJhY3QgYnkgYSBzdW1tYXJ5IHN0YXRpc3RpYyBzdWNoIGFzIHRoZSBtZWRpYW4gb2YgVG90YWxWYWx1ZS4gTm90aWNlIHdlIHVzZSB0aGUgYmFzZSBSIGZ1bmN0aW9uIGByZW9yZGVyYCB0byBhY2NvbXBsaXNoIHRoaXMuIFRoYXQgd2lsbCBwcm9kdWNlIGEgcmlkZ2VsaW5lIHBsb3Qgd2hlcmUgdGhlIGRpc3RyaWJ1dGlvbnMgYXJlIG9yZGVyZWQgYnkgbWVkaWFuIFRvdGFsVmFsdWUgb2YgZWFjaCBjZW5zdXMgdHJhY3QuIEJlbG93IHdlIHVzZSB0aGUgYGxhYnNgIGZ1bmN0aW9uIHRvIGFkZCBhIHRpdGxlIGFuZCB1cGRhdGUgdGhlIGF4aXMgbGFiZWxzLg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IFRvdGFsVmFsdWUsIHkgPSByZW9yZGVyKENlbnN1c1RyYWN0LCBUb3RhbFZhbHVlLCBtZWRpYW4pKSArIA0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzKCkgKw0KICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMWU2KSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpkb2xsYXIpICsNCiAgbGFicyh5ID0gIkNlbnN1cyBUcmFjdCIsIHggPSAiVG90YWwgVmFsdWUiLCANCiAgICAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgVG90YWwgVmFsdWUgYnkgQ2Vuc3VzIFRyYWN0IikgKw0KICB0aGVtZV9yaWRnZXMoKQ0KDQpgYGANCg0KTk9URTogYGdncGxvdGx5YCBkb2VzIG5vdCB3b3JrIHdpdGggcGxvdHMgbWFkZSBieSBnZ3JpZGdlcy4NCg0KIyMgQ3JlYXRpbmcgaW50ZXJhY3RpdmUgbWFwcyBpbiBSIHdpdGggbGVhZmxldA0KDQpXaGVuIHdlIHRhbGsgYWJvdXQgcmVhbCBlc3RhdGUgYW5kIGNlbnN1cyB0cmFjdHMsIGl0J3MgbmF0dXJhbCB0byB3YW50IHRvIHNlZSB2aXN1YWxpemF0aW9ucyB0aGF0IGluY29ycG9yYXRlIG1hcHMuIFdoZXJlIGlzIGNlbnN1cyB0cmFjdCAxMTAgaW4gQWxiZW1hcmxlIGNvdW50eT8gSG93IGJpZyBpcyBpdCBnZW9ncmFwaGljYWxseT8NCg0KVG8gY3JlYXRlIG1hcHMgd2UgdXN1YWxseSBuZWVkIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgdmFsdWVzIHNvIHdlIGNhbiBkcmF3IHNoYXBlcywgbGlrZSB0aGUgYm9yZGVycyBvZiBjb3VudGllcywgc3RhdGVzIGFuZCBjZW5zdXMgdHJhY3RzLiBPdXIgZGF0YSBkb2Vzbid0IGhhdmUgdGhhdCBpbmZvcm1hdGlvbiBzbyB3ZSBuZWVkIHRvIGdldCBpdC4gRm9ydHVuYXRlbHkgdGhlcmUgaXMgYW4gUiBwYWNrYWdlIHRoYXQgaGVscHMgdXMgcXVpY2tseSBnZXQgdGhpcyBkYXRhLiBUaGUgdGlncmlzIHBhY2thZ2UgYWxsb3dzIHVzIHRvIGRvd25sb2FkIFRJR0VSL0xpbmUgc2hhcGVmaWxlcyBmcm9tIHRoZSBVbml0ZWQgU3RhdGVzIENlbnN1cyBCdXJlYXUgYW5kIGxvYWQgaW50byBSIGFzICJzZiIgb2JqZWN0cy4gU0Ygc3RhbmRzIGZvciAic2ltcGxlIGZlYXR1cmVzIiB3aGljaCBpcyBhIHN0YW5kYXJkaXplZCB3YXkgb2YgZW5jb2Rpbmcgc3BhdGlhbCB2ZWN0b3IgZGF0YSBpbiBjb21wdXRlcnMuDQoNCkkgYWxyZWFkeSBkb3dubG9hZGVkIHRoZSBjZW5zdXMgdHJhY3QgYm9yZGVycyBhbmQgc2F2ZWQgYXMgYW4gcmRzIGZpbGUgZm9yIHVzIHRvIHVzZSB0byBoZWxwIHNhdmUgdGltZS4gVGhlIGNvZGUgSSB1c2VkIGlzIGJlbG93LCBjb21tZW50ZWQgb3V0LiBUaGUgZmluYWwgdW5jb21tZW50ZWQgbGluZSByZWFkcyBpbiB0aGUgZGF0YS4NCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoInRpZ3JpcyIpDQojIGxpYnJhcnkodGlncmlzKQ0KIyANCiMgdGhpcyBpcyBnb2luZyB0byBkb3dubG9hZCBhYm91dCAxNSBNQiBvZiBkYXRhOyBtYXkgdGFrZSBhIHNlY29uZA0KIyBhbGIgPC0gdHJhY3RzKHN0YXRlID0gIlZBIiwgY291bnR5ID0gIkFsYmVtYXJsZSIsIHllYXIgPSAyMDE5KQ0KDQojIGZvcm1hdCB0aGUgaW50ZXJuYWwgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB2YWx1ZXMgYXMgbnVtZXJpYy4gVGhlc2UgcmVwcmVzZW50DQojIHRoZSBnZW9ncmFwaGljIGNlbnRlciBvZiBlYWNoIGNlbnN1cyB0cmFjdC4NCg0KIyBhbGIgPC0gYWxiICU+JQ0KIyAgIG11dGF0ZShJTlRQVExPTiA9IGFzLm51bWVyaWMoSU5UUFRMT04pLA0KIyAgICAgICAgICBJTlRQVExBVCA9IGFzLm51bWVyaWMoSU5UUFRMQVQpKSAlPiUgDQojICAgc2Y6OnN0X3RyYW5zZm9ybShjcnMgPSAnK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQnKQ0KIyBzYXZlUkRTKGFsYiwgZmlsZSA9ICIuLi9kYXRhL2FsYi5SZHMiKQ0KDQphbGIgPC0gcmVhZFJEUyh1cmwoImh0dHBzOi8vZ2l0aHViLmNvbS91dmFzdGF0bGFiL0RKQS9yYXcvbWFpbi9kYXRhL2FsYi5SZHMiKSkNCg0KYGBgDQoNCk5vdyB0aGF0IHdlIGhhdmUgc3BhdGlhbCBkYXRhLCB3ZSdyZSByZWFkeSB0byBjcmVhdGUgYSBtYXAuIEZvciB0aGlzIHdlJ2xsIHVzZSB0aGUgbGVhZmxldCBwYWNrYWdlLiBUaGUgbGVhZmxldCBwYWNrYWdlIHRha2VzIGEgc3BhdGlhbCBkYXRhIGZyYW1lIGFuZCBhbGxvd3MgeW91IHRvIGxheWVyIG9uICJUaWxlcyIgYW5kICJQb2x5Z29ucyIgKGFtb25nIG11Y2ggZWxzZSkuIFRpbGVzIGFyZSBiYXNlIG1hcHMuIFRoZSBkZWZhdWx0IGlzIE9wZW5TdHJlZXRNYXAuIFBvbHlnb25zIGFyZSBzaGFwZXMgZHJhd24gd2l0aCB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBjb29yZGluYXRlcyBzdG9yZWQgaW4gdGhlIGBnZW9tZXRyeWAgY29sdW1uIG9mIHRoZSBgYWxiYCBkYXRhIGZyYW1lLiBJbiB0aGlzIGNhc2UgaXQgZHJhd3MgdGhlIGNlbnN1cyB0cmFjdCBib3VuZGFyaWVzLiBUaGUgY29kZSBiZWxvdyBwcm9kdWNlcyBhbiBpbnRlcmFjdGl2ZSBtYXAgeW91IGNhbiB6b29tIGluIGFuZCBtb3ZlIGFyb3VuZC4gDQoNCmBgYHtyfQ0KbGlicmFyeShsZWFmbGV0KQ0KbGVhZmxldChhbGIpICU+JSANCiAgYWRkVGlsZXMoKSAlPiUgDQogIGFkZFBvbHlnb25zKCkgDQpgYGANCg0KDQpUaGF0J3MgbmljZSBidXQgaXQgd291bGQgaGVscCBpZiB0aGUgY2Vuc3VzIHRyYWN0cyB3ZXJlIGxhYmVsZWQuIFdlIGNhbiBkbyB0aGF0IHdpdGggYGFkZExhYmVsT25seU1hcmtlcnMoKWAuIFdlIG5lZWQgdG8gbWFwIHRoZSBOQU1FIGNvbHVtbiBvZiB0aGUgYGFsYmAgZGF0YSBmcmFtZSB0byB0aGUgYGxhYmVsYCBhcmd1bWVudC4gU2V0IGBsbmcgPSBJTlRQVExPTmAgYW5kIGBsYXQgPSBJTlRQVExBVGAgdG8gcGxhY2UgbGFiZWxzIGF0IHRoZSBnZW9ncmFwaGljIGNlbnRlciBvZiB0aGUgY2Vuc3VzIHRyYWN0cy4gVGhlIGxlYWZsZXQgcGFja2FnZSByZXF1aXJlcyB3ZSBwbGFjZSBhIHRpbGRlIChgfmApIGluIGZyb250IG9mIHZhcmlhYmxlIG5hbWVzIHRvIGluZGljYXRlIHRoZXkncmUgbG9jYXRlZCBpbiB0aGUgZGF0YSBmcmFtZSBjYWxsZWQgaW4gdGhlIGBsZWFmbGV0YCBmdW5jdGlvbi4gQWdhaW4gdGhlIG1hcCB0aGF0IGlzIHJlbmRlcmVkIGlzIGludGVyYWN0aXZlLiANCg0KYGBge3J9DQpsZWFmbGV0KGFsYikgJT4lIA0KICBhZGRUaWxlcygpICU+JSANCiAgYWRkUG9seWdvbnMoKSAlPiUgDQogIGFkZExhYmVsT25seU1hcmtlcnMobG5nID0gfklOVFBUTE9OLCBsYXQgPSB+SU5UUFRMQVQsIGxhYmVsID0gfk5BTUUsDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxPcHRpb25zID0gbGFiZWxPcHRpb25zKG5vSGlkZSA9IFRSVUUpKQ0KDQpgYGANCg0KDQpJZiB3ZSBsaWtlIHdlIGNhbiBzaGFkZSB0aGUgY2Vuc3VzIHRyYWN0IHJlZ2lvbnMgYWNjb3JkaW5nIHRvIGEgc3VtbWFyeSBzdGF0aXN0aWMgc3VjaCBhcyBtZWRpYW4gdG90YWwgdmFsdWUgb2YgYSBob21lLiBMZXQncyBkbyB0aGF0ISBGaXJzdCB3ZSBjYWxjdWxhdGUgbWVkaWFuIFRvdGFsVmFsdWUgYnkgY2Vuc3VzIHRyYWN0LCBhbmQgdGhlbiBqb2luIHdpdGggdGhlIGBhbGJgIGRhdGEgZnJhbWUuDQoNCg0KYGBge3J9DQptZWRfdHYgPC0gaG9tZXMgJT4lDQogIGZpbHRlcighaXMubmEoQ2Vuc3VzVHJhY3QpKSAlPiUgDQogIGdyb3VwX2J5KENlbnN1c1RyYWN0KSAlPiUgDQogIHN1bW1hcml6ZShUb3RhbFZhbHVlID0gbWVkaWFuKFRvdGFsVmFsdWUpKQ0KDQphbGJfbWVkX3R2IDwtIGxlZnRfam9pbihhbGIsIG1lZF90diwgYnkgPSBjKCJOQU1FIiA9ICJDZW5zdXNUcmFjdCIpKSAlPiUgDQogIGZpbHRlcighaXMubmEoVG90YWxWYWx1ZSkpDQoNCmBgYA0KDQpUbyBzaGFkZSB0aGUgY2Vuc3VzIHRyYWN0IHJlZ2lvbnMgd2Ugb25jZSBhZ2FpbiB1c2UgYGFkZFBvbHlnb25zKClgLiBUaGlzIHRpbWUgd2UgYXNzaWduIHRoZSBgbGFiZWxgIGFyZ3VtZW50IHRvIFRvdGFsVmFsdWUgdG8gZGlzcGxheSB0aGUgbWVkaWFuIGhvbWUgdmFsdWUgd2hlbiB3ZSBob3ZlciBvdmVyIGEgY2Vuc3VzIHRyYWN0LiBXZSBhbHNvIGFkZCBhIGxlZ2VuZCB1c2luZyBgYWRkTGVnZW5kKClgLiBGb3IgYm90aCBvZiB0aGVzZSB3ZSB1c2UgYSBjb2xvciBwYWxldHRlIGZ1bmN0aW9uIHRoYXQgd2UgY3JlYXRlIHdpdGggdGhlIGBjb2xvck51bWVyaWMoKWAgZnVuY3Rpb24uIFRoYXQgZ2VuZXJhdGVzIGEgY29sb3IgcGFsZXR0ZSBiYXNlZCBvbiB0aGUgbnVtZXJpYyBkYXRhIHdlIGdpdmUgaXQuIA0KDQpgYGB7cn0NCiMgY3JlYXRlIGNvbG9yIHBhbGV0dGUgZm9yIHNoYWRpbmcgY2Vuc3VzIHRyYWN0cw0KcGFsIDwtICBjb2xvck51bWVyaWMoIllsT3JSZCIsIGFsYiRUb3RhbFZhbHVlKQ0KbGVhZmxldChhbGJfbWVkX3R2KSAlPiUgDQogIGFkZFRpbGVzKCkgJT4lIA0KICBhZGRQb2x5Z29ucygpICU+JSANCiAgYWRkTGFiZWxPbmx5TWFya2VycyhsbmcgPSB+SU5UUFRMT04sIGxhdCA9IH5JTlRQVExBVCwgbGFiZWwgPSB+TkFNRSwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbE9wdGlvbnMgPSBsYWJlbE9wdGlvbnMobm9IaWRlID0gVFJVRSkpICU+JSANCiAgYWRkUG9seWdvbnMoc3Ryb2tlID0gRkFMU0UsIGZpbGxPcGFjaXR5ID0gMC41LCANCiAgICAgICAgICAgICAgc21vb3RoRmFjdG9yID0gMC41LCANCiAgICAgICAgICAgICAgY29sb3IgPSB+cGFsKFRvdGFsVmFsdWUpLA0KICAgICAgICAgICAgICBsYWJlbCA9IH5Ub3RhbFZhbHVlKSAlPiUgDQogIGFkZExlZ2VuZChwYWwgPSBwYWwsIA0KICAgICAgICAgICAgdmFsdWVzID0gflRvdGFsVmFsdWUsIA0KICAgICAgICAgICAgb3BhY2l0eSA9IDAuOSwgDQogICAgICAgICAgICB0aXRsZSA9ICJNZWRpYW4gSG9tZSBWYWx1ZSIsIA0KICAgICAgICAgICAgcG9zaXRpb24gPSAiYm90dG9tbGVmdCIpDQoNCmBgYA0KDQoNCiMjIFdlJ3JlIGRvbmUhDQoNCklmIHlvdSB3b3VsZCBsaWtlIHRvIHRhbGsgbW9yZSBhYm91dCBnZ3Bsb3QyIG9yIGFueXRoaW5nIGVsc2Ugc3RhdGlzdGljcyByZWxhdGVkLCBJIHdvdWxkIGxvdmUgdG8gaGVhciBmcm9tIHlvdTogYGNsYXlmb3JkQHZpcmdpbmlhLmVkdWAgb3IgYHN0YXRsYWJAdmlyZ2luaWEuZWR1YA0KDQoNCiMjIFJlZmVyZW5jZXMNCg0KLSBXaWNraGFtLCBILiAoMjAxNiksICpnZ3Bsb3QyOiBFbGVnYW50IEdyYXBoaWNzIGZvciBEYXRhIEFuYWx5c2lzLCAybmQgZWQqLCBTcHJpbmdlci4gaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnLyAob24tbGluZSB2ZXJzaW9uIG9mIHdvcmstaW4tcHJvZ3Jlc3MgM3JkIGVkaXRpb24pDQoNCi0gV2lja2hhbSwgSC4gYW5kIEdyb2xlbXVuZCBHLiAoMjAxNyksICpSIGZvciBEYXRhIFNjaWVuY2UqLiBPJ1JlaWxseS4gaHR0cDovL3I0ZHMuaGFkLmNvLm56Lw0KDQotICAgQ2hlbmcgSiwgS2FyYW1iZWxrYXIgQiwgWGllIFkgKDIwMjIpLiBfbGVhZmxldDogQ3JlYXRlIEludGVyYWN0aXZlIFdlYiBNYXBzIHdpdGggdGhlDQogIEphdmFTY3JpcHQgJ0xlYWZsZXQnIExpYnJhcnlfLiBSIHBhY2thZ2UgdmVyc2lvbiAyLjEuMSwNCiAgPGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9bGVhZmxldD4uDQogIA0KLSAgIEMuIFNpZXZlcnQuIEludGVyYWN0aXZlIFdlYi1CYXNlZCBEYXRhIFZpc3VhbGl6YXRpb24gd2l0aCBSLCBwbG90bHksIGFuZCBzaGlueS4NCiAgQ2hhcG1hbiBhbmQgSGFsbC9DUkMgRmxvcmlkYSwgMjAyMC4gPGh0dHBzOi8vcGxvdGx5LXIuY29tLz4NCg0K